#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/usb.h>

/**
 *  USB Mass Storage Class
 *
 * https://www.oreilly.com/library/view/linux-device-drivers/0596005903/ch13.html
 *
 * https://www.kernel.org/doc/html/v4.12/driver-api/usb/writing_usb_driver.html
 *
 * http://www.linux-usb.org/
 *
 * https://embetronicx.com/tutorials/linux/device-drivers/usb-device-driver-basics/
 *
 * https://github.com/Embetronicx/Tutorials/blob/master/Linux/Device_Driver/usb_device_driver/usb_driver.c
 *
 * https://www.linux.it/~rubini/docs/usb/usb.html
 *
 * http://www.linux-usb.org/devices.html
 *
 * https://kernel.dk/blk-mq.pdf
 *
 * A block is a fixed-size chunk of data, the size being determined by the kernel.
 * Blocks are often 4096 bytes, but that value can vary depending
 * on the architecture and the exact filesystem being used
 *
 *
 * Devices usually have one or more configurations.
 *
 *
 * Whenever a USB device is attached to the bus it will be enumerated by the
 USB subsystem - i.e an unique device number (1-127) is assigned and then the
 device descriptor is read. Such a desciptor is a data structure which contains
 information about the device and its properties.


 Configurations often have one or more interfaces.

 Interfaces usually have one or more settings.

 Interfaces have zero or more endpoints.
 *
 *
 *  lsusb
 *  usb-devices
 *
 */

//#define USB_VENDOR_ID  (0x1058)
//#define USB_PRODUCT_ID (0x2621)
#define USB_VENDOR_ID  (0x0951)
#define USB_PRODUCT_ID (0x1666)

#define PRINT_USB_ENDPOINT_DESCRIPTOR( i )                         \
{                                                                   \
    pr_info("USB_INTERFACE_DESCRIPTOR:\n");                         \
    pr_info("-----------------------------\n");                     \
    pr_info("bLength: 0x%x\n", i.bLength);                          \
    pr_info("bDescriptorType: 0x%x\n", i.bDescriptorType);          \
    pr_info("bInterfaceNumber: 0x%x\n", i.bInterfaceNumber);        \
    pr_info("bAlternateSetting: 0x%x\n", i.bAlternateSetting);      \
    pr_info("bNumEndpoints: 0x%x\n", i.bNumEndpoints);              \
    pr_info("bInterfaceClass: 0x%x\n", i.bInterfaceClass);          \
    pr_info("bInterfaceSubClass: 0x%x\n", i.bInterfaceSubClass);    \
    pr_info("bInterfaceProtocol: 0x%x\n", i.bInterfaceProtocol);    \
    pr_info("iInterface: 0x%x\n", i.iInterface);                    \
    pr_info("\n");                                                  \
}

//usb_host_endpoint

//usb_endpoint_descriptor

struct vdsk_dev {
	unsigned long size;
	u8 *data;
	spinlock_t lock;
	struct gendisk *mydisk;
	struct request_queue *queue;
};

struct vdsk_dev *dev;







int myprobe(struct usb_interface *interface, const struct usb_device_id *id) {
	unsigned int i;
	unsigned int endpoints_count;

	int endpoint_type;

	int  interfaceClass;

	struct usb_endpoint_descriptor endpoint_descriptor;
	struct usb_host_interface *iface_desc = interface->cur_altsetting;
	dev_info(&interface->dev, "andy myprobe\n");
	dev_info(&interface->dev, "idVendor = %x\n", id->idVendor);
	dev_info(&interface->dev, "idProduct = %x\n", id->idProduct);

	endpoints_count = iface_desc->desc.bNumEndpoints;
	dev_info(&interface->dev, "endpoints_count = %d\n", endpoints_count);

	interfaceClass = iface_desc->desc.bInterfaceClass ;
	dev_info(&interface->dev, "bInterfaceClass = %d\n",interfaceClass);


	switch (interfaceClass) {
			case USB_CLASS_MASS_STORAGE:
				dev_info(&interface->dev, "interfaceClass: USB_CLASS_MASS_STORAGE\n");
				break;
			default:
				dev_info(&interface->dev, "never \n");
				break;
	}

	for (i = 0; i < endpoints_count; i++) {
		endpoint_descriptor = iface_desc->endpoint[i].desc;
		dev_info(&interface->dev, "endpoint %d bEndpointAddress = %d\n", i,
				endpoint_descriptor.bEndpointAddress);
		endpoint_type = endpoint_descriptor.bmAttributes
				& USB_ENDPOINT_XFERTYPE_MASK;

		switch (endpoint_type) {
		case USB_ENDPOINT_XFER_CONTROL:
			dev_info(&interface->dev, "control endpoint\n");
			break;
		case USB_ENDPOINT_XFER_ISOC:
			dev_info(&interface->dev, "ISOCHRONOUS endpoint\n");
			break;
		case USB_ENDPOINT_XFER_BULK:
			dev_info(&interface->dev, "bulk endpoint\n");
			break;
		case USB_ENDPOINT_XFER_INT:
			dev_info(&interface->dev, "Interrupt  endpoint\n");
			break;
		default:
			dev_info(&interface->dev, "never \n");
			break;
		}
	}

	return 0;
}

void mydisconnect(struct usb_interface *intf) {
	dev_info(&intf->dev, "mydisconnect\n");
}

static struct usb_device_id my_id_table[] = { { USB_DEVICE(USB_VENDOR_ID,
		USB_PRODUCT_ID) }, { } };

MODULE_DEVICE_TABLE(usb, my_id_table);

static struct usb_driver my_usbdriver = { .name = "andy_usbdriver", .probe =
		myprobe, .disconnect = mydisconnect, .id_table = my_id_table, };

module_usb_driver( my_usbdriver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andy");
